package raft

import (
	"bytes"
	"edata-hub/pkg/raft/services"
	"encoding/gob"
	"golang.org/x/net/context"
)

// @Description: 作为Leader时，向Peers中的所有节点复制日志
// @receiver r
func (r *Raft) AppendEntries() {
	req := new(services.AppendEntriesReq)
	req.Address = r.Addr
	req.Term = r.Term
	for k, p := range r.Peers {
		r.Msg.Debug("%s sending heartbeat to %s", r.Addr, k)
		if p.Addr == r.Addr {
			r.ResetElectionTime()
			continue
		}
		//根据各Follower的最新日志index计算出Leader的日志子集。
		//但是无法保证该Leader所记录的Follower的日志index就是那个Follower所记录的最新index
		//所以日志子集转成二进制发送给各个Follower后，仍需要逐个比对。
		logEntries := r.Log.slice(p.LastLogIndex)
		writer := new(bytes.Buffer)
		encoder := gob.NewEncoder(writer)
		encodeErr := encoder.Encode(logEntries)
		if encodeErr != nil {
			r.Msg.Error("encode log entries fail:%s", encodeErr)
		}
		b := writer.Bytes()
		req.Data = b
		logEntry := r.Log.at(p.LastLogIndex)
		req.LastLogIndex = logEntry.Index
		req.LastLogTerm = logEntry.Term

		go func(remote string, req *services.AppendEntriesReq) {
			client, _ := r.CreateGRPCClient(remote)
			resp, err := client.OnHandleAppendEntries(context.Background(), req)
			if err != nil {
				r.Msg.Error("%s can not send log entries to %s", r.Addr, remote)
				return
			}
			//如果心跳发送过去之后发现 Follower 的任期比 Leader 更高，则 Leader 变成 Follower
			if resp.Term > r.Term {
				r.ChangeRoleChan <- Follower
				return
			}
			p := Peer{
				Addr: r.Peers[remote].Addr,
				Role: r.Peers[remote].Role,
			}
			if resp.Success {
				p.LastLogIndex = resp.LastLogIndex
				r.Peers[remote] = p
			} else if resp.Conflict {
				r.Msg.Error("%s appending entries conflict with %s", r.Addr, remote)
				if resp.XTerm == -1 {
					p.LastLogIndex = resp.LastLogIndex
					r.Peers[remote] = p
				} /*else {
					//根据 Follower 传来的有问题的 Term 找到对应最后的日志 Index
					//该Index在下一次AppendEntries传给Follower，届时可能还会继续触发本条件
					//直到Leader和Follower两边日志的Term和Index完全一致。
					ind := r.Ls.findLastLogIndexByTerm(resp.XTerm)
					if ind > 0 {
						r.Peers[p] = ind
					} else {
						//如果上一次
						r.Peers[p] = resp.XIndex
					}
				}*/
			}
		}(p.Addr, req)
	}
}
